/*
 * COPYRIGHT:	2010 Christopher Smith, licenced under the GNU GPL v2; see COPYING in the top level directory
 * PROJECT:		sdedit
 * FILE:		stdsid.cpp
 * PURPOSE:		well-known, token and logon sid enumeration and translation sources
 *				
 */

namespace StdSid
{
using namespace Lookup;

template <class T> void CompleteExceptNotFound(CompletionTarget<T> *Ct)
{
	__try
	{
		RaiseNtError(STATUS_NONE_MAPPED);
	}
	__except(CompleteExceptSEHFilter(Ct, (exception_info())->ExceptionRecord))
	{}
}

struct LogonSidTranslator : Translator<SID>
{
	static void LookupLogonSessionData(LUID const &LogonLuid, WCHAR (&Text)[MAX_STRING_CHARS])
	{
		auto_ptr<SECURITY_LOGON_SESSION_DATA, Lsa::LsaHeap> Lsd;

		RaiseOnNtError(LsaGetLogonSessionData(const_cast<LUID *>(&LogonLuid), Lsd.assign_indirect()));

		//verify that the required fields are in fact present
		if(Lsd->Size < RTL_SIZEOF_THROUGH_FIELD(SECURITY_LOGON_SESSION_DATA, LogonDomain))
			RaiseOnNtError(STATUS_NOT_IMPLEMENTED);

		auto_szpod<WCHAR> LogonDomain = Lsa::UNICODE_STRING2NewPWSTR(&Lsd->LogonDomain);
		auto_szpod<WCHAR> UserName = Lsa::UNICODE_STRING2NewPWSTR(&Lsd->UserName);

		swprintf_s(Text, L"Logon session #%.0lu%lu of %s\\%s", Lsd->LogonId.LowPart, Lsd->LogonId.HighPart, LogonDomain, UserName);
	}

	static void LookupBasicLogonSessionData(LUID const &LogonLuid, WCHAR (&Text)[MAX_STRING_CHARS])
	{
		swprintf_s(Text, L"Logon session #%.0lu%lu", LogonLuid.LowPart, LogonLuid.HighPart);
	}

	static void LookupToText(SID const *Sid, WCHAR (&Text)[MAX_STRING_CHARS])
	{
		LUID LogonLuid = {Sid->SubAuthority[2], Sid->SubAuthority[1]};
	
		__try
		{
			LookupLogonSessionData(LogonLuid, Text);
		}
		__except(
			exception_code() == STATUS_NT_ERROR ||
			exception_code() == STATUS_VCPP_MOD_NOT_FOUND ||
			exception_code() == STATUS_VCPP_PROC_NOT_FOUND ?
			EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
		{
			LookupBasicLogonSessionData(LogonLuid, Text);
		}
	}

	virtual void LookupData(CompletionTarget<WCHAR> *Ct, SID const *Sid) override
	{
		if(Sid->Revision == 1 &&
			Sid->IdentifierAuthority.Value[5] == 5 && //NT AUTHORITY
			Sid->SubAuthorityCount == 3 && //logon sid has two 32 bit parts, plus the "logon sid" rid
			Sid->SubAuthority[0] == 5) //logon sids
		{
			WCHAR Text[MAX_STRING_CHARS];

			LookupToText(Sid, Text);

			Ct->Complete(Text);
		}
		else
			CompleteExceptNotFound(Ct);
	}

	virtual void LookupName(CompletionTarget<SID> *Ct, PCWSTR Name) override
	{
		CompleteExceptNotFound(Ct);
	}
};

struct TokenSidEnumerator : public Enumerator<SID>
{
	virtual void WalkChildren(ChildrenWalkDest<SID> &Dest) override
	{}//no children

	template <class T> void QueryToken(HANDLE Token, TOKEN_INFORMATION_CLASS Class, auto_ptr<T> &Buff)
	{
		ULONG InfoLen = TOKEN_INFO_SIZE_DEFAULT;

		while(TRUE)
		{
			Buff.realloc(InfoLen);

			NTSTATUS Status = NtQueryInformationToken(Token, Class, Buff, InfoLen, &InfoLen);

			if(Status == STATUS_BUFFER_TOO_SMALL || Status == STATUS_BUFFER_OVERFLOW)
				continue;

			RaiseOnNtError(Status);
			break;
		}
	}

	virtual void WalkEntries(EntriesWalkDest<SID> &Dest)
	{
		auto_free<VOID, free_ExecutiveHandle> Token;

		NTSTATUS Status = NtOpenThreadToken(reinterpret_cast<HANDLE>(-1)/*self*/, TOKEN_QUERY, FALSE, Token.assign_indirect());

		if(NT_FAILURE(Status))
			RaiseOnNtError(NtOpenProcessToken(reinterpret_cast<HANDLE>(-1)/*self*/, TOKEN_QUERY, Token.assign_indirect()));

		{
			auto_ptr<TOKEN_USER> User;

			QueryToken(Token, TokenUser, User);

			Dest.EntryProc(L"Self", *reinterpret_cast<SID *>(User->User.Sid));
		}

		{
			auto_ptr<TOKEN_PRIMARY_GROUP> Group;

			QueryToken(Token, TokenPrimaryGroup, Group);

			Dest.EntryProc(L"Primary Group", *reinterpret_cast<SID *>(Group->PrimaryGroup));
		}

		{
			auto_ptr<TOKEN_GROUPS> Groups;
			auto_refct<Lsa::LsaPolicy> Policy = new Lsa::LsaPolicy(NULL, 0);

			QueryToken(Token, TokenGroups, Groups);

			auto_array<PSID> Sids(Groups->GroupCount);
			auto_ptr<LSA_REFERENCED_DOMAIN_LIST, Lsa::LsaHeap> ReferencedDomains;
			auto_ptr<LSA_TRANSLATED_NAME, Lsa::LsaHeap> TranslatedNames;

			for(ULONG i = 0; i < Groups->GroupCount; i++)
				Sids[i] = Groups->Groups[i].Sid;

			RaiseOnNtError(LsaLookupSids(Policy->Handle, Groups->GroupCount, Sids, ReferencedDomains.assign_indirect(), TranslatedNames.assign_indirect()));

			for(ULONG i = 0; i < Groups->GroupCount; i++)
			{
				if(Groups->Groups[i].Attributes & SE_GROUP_LOGON_ID)
				{
					WCHAR Text[MAX_STRING_CHARS];

					LogonSidTranslator::LookupToText(reinterpret_cast<SID *>(Sids[i]), Text);

					Dest.EntryProc(Text, *reinterpret_cast<SID *>(Sids[i]));
				}
				else
				{
					auto_szpod<WCHAR> Pwstr;

					if(TranslatedNames[i].Name.Length)
						Pwstr = Lsa::UNICODE_STRING2NewPWSTR(&TranslatedNames[i].Name);
					else
						Pwstr = SidToText(reinterpret_cast<SID *>(Sids[i]));

					Dest.EntryProc(Pwstr, *reinterpret_cast<SID *>(Sids[i]));
				}
			}
		}
	}
};

struct WellKnownNonUniqueSidEnumerator : public Enumerator<SID>
{
	virtual void WalkChildren(ChildrenWalkDest<SID> &Dest) override
	{}//no children

	static void StaticWalkEntries(EntriesWalkDest<SID> &Dest)
	{
		union
		{
			SID Sid;
			CHAR SidBytes[MAX_SID_BYTES];
		};
		RtlZeroMemory(SidBytes, MAX_SID_BYTES);

		Sid.Revision = 1;

		//null authority
		Sid.IdentifierAuthority.Value[5] = 0;
		Sid.SubAuthorityCount = 1;
		Sid.SubAuthority[0] = 0;
		Dest.EntryProc(L"Nobody", Sid);

		//world authority
		Sid.IdentifierAuthority.Value[5] = 1;
		Sid.SubAuthorityCount = 1;
		Sid.SubAuthority[0] = 0;
		Dest.EntryProc(L"Everyone", Sid);

		//local authority
		Sid.IdentifierAuthority.Value[5] = 2;
		Sid.SubAuthorityCount = 1;
		Sid.SubAuthority[0] = 0;
		Dest.EntryProc(L"Local", Sid);

		//creator authority
		Sid.IdentifierAuthority.Value[5] = 3;
		Sid.SubAuthorityCount = 1;
		Sid.SubAuthority[0] = 0;
		Dest.EntryProc(L"Creator Owner", Sid);
		Sid.SubAuthority[0] = 1;
		Dest.EntryProc(L"Creator Group", Sid);
		Sid.SubAuthority[0] = 2;
		Dest.EntryProc(L"Creator Owner Server", Sid);
		Sid.SubAuthority[0] = 3;
		Dest.EntryProc(L"Creator Group Server", Sid);
		Sid.SubAuthority[0] = 4;
		Dest.EntryProc(L"Owner Rights", Sid);

		//nonunique authority (#4) not used

		Dest.BreakProc();
		//NT authority
		Sid.IdentifierAuthority.Value[5] = 5;
		Sid.SubAuthorityCount = 1;
		Sid.SubAuthority[0] = 1;
		Dest.EntryProc(L"Dialup", Sid);
		Sid.SubAuthority[0] = 2;
		Dest.EntryProc(L"Network", Sid);
		Sid.SubAuthority[0] = 3;
		Dest.EntryProc(L"Batch", Sid);
		Sid.SubAuthority[0] = 4;
		Dest.EntryProc(L"Interactive", Sid);
		Sid.SubAuthority[0] = 6;
		Dest.EntryProc(L"Service", Sid);
		Sid.SubAuthority[0] = 7;
		Dest.EntryProc(L"Anonymous", Sid);
		Sid.SubAuthority[0] = 8;
		Dest.EntryProc(L"Proxy", Sid);
		Sid.SubAuthority[0] = 9;
		Dest.EntryProc(L"Enterprise Domain Controllers", Sid);
		Sid.SubAuthority[0] = 10;
		Dest.EntryProc(L"Principal Self", Sid);
		Sid.SubAuthority[0] = 11;
		Dest.EntryProc(L"Authenticated Users", Sid);
		Sid.SubAuthority[0] = 12;
		Dest.EntryProc(L"Restricted Code", Sid);
		Sid.SubAuthority[0] = 13;
		Dest.EntryProc(L"Terminal Server Users", Sid);
		Sid.SubAuthority[0] = 14;
		Dest.EntryProc(L"Remote Interactive Logon", Sid);
		Sid.SubAuthority[0] = 15;
		Dest.EntryProc(L"This Organization", Sid);
		Sid.SubAuthority[0] = 18;
		Dest.EntryProc(L"Local System", Sid);
		Sid.SubAuthority[0] = 19;
		Dest.EntryProc(L"Local Service", Sid);
		Sid.SubAuthority[0] = 20;
		Dest.EntryProc(L"Network Service", Sid);

		Sid.SubAuthorityCount = 2;
		Sid.SubAuthority[0] = 32;
		Sid.SubAuthority[1] = 544;
		Dest.EntryProc(L"Administrators", Sid);
		Sid.SubAuthority[1] = 545;
		Dest.EntryProc(L"Users", Sid);
		Sid.SubAuthority[1] = 546;
		Dest.EntryProc(L"Guests", Sid);
		Sid.SubAuthority[1] = 547;
		Dest.EntryProc(L"Power Users", Sid);
		Sid.SubAuthority[1] = 548;
		Dest.EntryProc(L"Account Operators", Sid);
		Sid.SubAuthority[1] = 549;
		Dest.EntryProc(L"Server Operators", Sid);
		Sid.SubAuthority[1] = 550;
		Dest.EntryProc(L"Print Operators", Sid);
		Sid.SubAuthority[1] = 551;
		Dest.EntryProc(L"Backup Operators", Sid);
		Sid.SubAuthority[1] = 552;
		Dest.EntryProc(L"Replicators", Sid);

		Dest.BreakProc();
		Sid.SubAuthority[1] = 554;
		Dest.EntryProc(L"Pre-Windows 2000 Compatible Access", Sid);
		Sid.SubAuthority[1] = 555;
		Dest.EntryProc(L"Remote Desktop Users", Sid);
		Sid.SubAuthority[1] = 556;
		Dest.EntryProc(L"Network Configuration Operators", Sid);
		Sid.SubAuthority[1] = 557;
		Dest.EntryProc(L"Incoming Forest Trust Builders", Sid);
		Sid.SubAuthority[1] = 558;
		Dest.EntryProc(L"Performance Monitor Users", Sid);
		Sid.SubAuthority[1] = 559;
		Dest.EntryProc(L"Performance Log Users", Sid);
		Sid.SubAuthority[1] = 560;
		Dest.EntryProc(L"Windows Authorization Access Group", Sid);
		Sid.SubAuthority[1] = 561;
		Dest.EntryProc(L"Terminal Server License Servers", Sid);

		Dest.BreakProc();
		//mandatory label authority
		Sid.IdentifierAuthority.Value[5] = 16;
		Sid.SubAuthorityCount = 1;

		Sid.SubAuthority[0] = 0x0000;
		Dest.EntryProc(L"Untrusted Integrity Label", Sid);
		Sid.SubAuthority[0] = 0x1000;
		Dest.EntryProc(L"Low Integrity Label", Sid);
		Sid.SubAuthority[0] = 0x2000;
		Dest.EntryProc(L"Medium Integrity Label", Sid);
		Sid.SubAuthority[0] = 0x3000;
		Dest.EntryProc(L"High Integrity Label", Sid);
		Sid.SubAuthority[0] = 0x4000;
		Dest.EntryProc(L"System Integrity Label", Sid);
		Sid.SubAuthority[0] = 0x5000;
		Dest.EntryProc(L"Protected Integrity Label", Sid);
	}

	virtual void WalkEntries(EntriesWalkDest<SID> &Dest)
	{
		StaticWalkEntries(Dest);
	}
};

struct WellKnownNonUniqueSidTranslator : Translator<SID>
{
	struct EndEnumException {};

	struct LookupSidSearch : EntriesWalkDest<SID>
	{
		auto_refct<CompletionTarget<WCHAR> > Ct;
		SID const *Sid;
		LookupSidSearch(CompletionTarget<WCHAR> *Ct, SID const *Sid) :
			Ct(Ct),
			Sid(Sid)
		{}

		virtual void EntryProc(PCWSTR Name, SID const &Data) override
		{
			if(EqualSid(const_cast<SID *>(Sid), const_cast<SID *>(&Data)))
			{
				Ct->Complete(Name);
				throw EndEnumException();
			}
		}

		virtual void BreakProc() override
		{}

		LookupSidSearch &operator=(LookupSidSearch const &); //no assignment
	};

	virtual void LookupData(CompletionTarget<WCHAR> *Ct, SID const *Sid) override
	{
		LookupSidSearch Search(Ct, Sid);

		try
		{
			WellKnownNonUniqueSidEnumerator::StaticWalkEntries(Search);

			CompleteExceptNotFound(Ct); //didn't exit: couldn't find item
		}
		catch(EndEnumException)
		{}
	}

	struct LookupNameSearch : EntriesWalkDest<SID>
	{
		auto_refct<CompletionTarget<SID> > Ct;
		PCWSTR LookupName;
		LookupNameSearch(CompletionTarget<SID> *Ct, PCWSTR LookupName) :
			Ct(Ct),
			LookupName(LookupName)
		{}

		virtual void EntryProc(PCWSTR Name, SID const &Data) override
		{
			if(_wcsicmp(LookupName, Name) == 0)
			{
				Ct->Complete(&Data);
				throw EndEnumException();
			}
		}

		virtual void BreakProc() override
		{}
	};

	virtual void LookupName(CompletionTarget<SID> *Ct, PCWSTR Name) override
	{
		LookupNameSearch Search(Ct, Name);

		try
		{
			WellKnownNonUniqueSidEnumerator::StaticWalkEntries(Search);

			CompleteExceptNotFound(Ct); //didn't exit: couldn't find item
		}
		catch(EndEnumException)
		{}
	}
};

}//namespace StdSid